/*
 * This file is part of Cadmium.
 * Copyright (C) 2007-2010 Xavier Clerc.
 *
 * Cadmium is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Cadmium is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.x9c.cadmium.primitives.stdlib;

import java.lang.reflect.Method;

import fr.x9c.cadmium.kernel.Block;
import fr.x9c.cadmium.kernel.CodeRunner;
import fr.x9c.cadmium.kernel.Fail;
import fr.x9c.cadmium.kernel.Primitive;
import fr.x9c.cadmium.kernel.PrimitiveProvider;
import fr.x9c.cadmium.kernel.Value;

/**
 * Implements all primitives from 'dynlink.c'.
 *
 * @author <a href="mailto:cadmium@x9c.fr">Xavier Clerc</a>
 * @version 1.0
 * @since 1.0
 */
@PrimitiveProvider
public final class Dynlink {

    /**
     * No instance of this class.
     */
    private Dynlink() {
    } // end empty constructor

    /**
     * If passed argument represents a file name, then nothing is done
     * (supposing that primitives will be provided by another mean).
     * Otherwise, the argument is interpreted as the fully qualified name
     * of a Java class ({@link fr.x9c.cadmium.kernel.PrimitiveProvider})
     * that is loaded.
     * @param ctxt context
     * @param mode ignored
     * @param filename library identifier
     * @return library handle
     * @throws Fail.Exception <i>Failure</i> if it is impossible to load
     *         the library
     */
    @Primitive
    public static Value caml_dynlink_open_lib(final CodeRunner ctxt,
                                              final Value mode,
                                              final Value filename)
        throws Fail.Exception {
        return ctxt.getContext().openLib(filename);
    } // end method 'caml_dynlink_open_lib(CodeRunner, Value, Value)'

    /**
     * Only removes the library from the list of opened libraries.
     * @param ctxt context
     * @param handle library handle
     * @return <i>unit</i>
     */
    @Primitive
    public static Value caml_dynlink_close_lib(final CodeRunner ctxt,
                                               final Value handle) {
        ctxt.getContext().closeLib(handle.asBlock().get(0).asBlock().asString());
        return Value.UNIT;
    } // end method 'caml_dynlink_close_lib(CodeRunner, Value)'

    /**
     * Searches for a primitive in a library.
     * @param ctxt context
     * @param libHandle library handle
     * @param symbName primitive name
     * @return an abstract block with primitives name and implementation,
     *         <i>unit</i> if not found
     */
    @Primitive
    public static Value caml_dynlink_lookup_symbol(final CodeRunner ctxt,
                                                   final Value libHandle,
                                                   final Value symbName) {
        final String lib = libHandle.asBlock().get(0).asBlock().asString();
        final String name = symbName.asBlock().asString();
        return ctxt.getContext().lookupPrimitive(lib, name);
    } // end method 'caml_dynlink_lookup_symbol(CodeRunner, Value, Value)'

    /**
     * Adds a primitive to the context.
     * @param ctxt context
     * @param prim primitive to add to context, as returned by
     *        {@link #caml_dynlink_lookup_symbol(CodeRunner, Value, Value)}
     * @return the integer identifier of the added primitive
     * @throws Fail.Exception if the code runner is native
     */
    @Primitive
    public static Value caml_dynlink_add_primitive(final CodeRunner ctxt,
                                                   final Value prim)
        throws Fail.Exception {
        if (ctxt.isNative()) {
            Fail.invalidArgument("dynlink_add_primitive");
        } // end if
        final Block bl = prim.asBlock();
        final String name = bl.get(0).asBlock().asString();
        final Method meth = (Method) bl.asCustom();
        return Value.createFromLong(ctxt.getContext().addPrimitive(name, meth));
    } // end method 'caml_dynlink_add_primitive(CodeRunner, Value)'

    /**
     * Returns a block/array containing all the opened libraries.
     * @param ctxt context
     * @param unit ignored
     * @return a block/array containing the handles of all opened libraries
     * @throws Fail.Exception if the code runner is native
     */
    @Primitive
    public static Value caml_dynlink_get_current_libs(final CodeRunner ctxt,
                                                      final Value unit)
        throws Fail.Exception {
        if (ctxt.isNative()) {
            Fail.invalidArgument("dynlink_get_current_libs");
        } // end if
        return ctxt.getContext().makeLibsArray();
    } // end method 'caml_dynlink_get_current_libs(CodeRunner, Value)'

} // end class 'Dynlink'
